package com.wmx.es.controller;

import com.wmx.es.entity.Agency;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.elasticsearch.index.query.*;
import org.junit.Test;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.data.web.PageableDefault;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ElasticsearchTemplate 全文检索 elasticsearch
 *
 * @author wangmaoxiong
 * @version 1.0
 * @date 2020/6/4 16:58
 */
@RestController
public class EsTemplate {
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 创建索引和映射
     * http://localhost:8080/est/agency/createIndex
     *
     * @return
     */
    @GetMapping("est/agency/createIndex")
    public Map<String, Object> createIndex() {
        // 根据实体类与属性上的注解 创建索引，返回是否创建成功。已经存在时，直接返回 true。
        boolean createIndex = elasticsearchTemplate.createIndex(Agency.class);
        // 根据实体类与属性上的注解 创建映射关系
        // 1、如果实体类中没有指定主键(@Id)注解，则创建报错：Failed to build mapping for Agency
        // Required identifier property not found for class com.wmx.es.entity.Agency!
        // 2、索引已经存在数据时，则不允许再修改映射关系，否则报错： Mapper for [xxx] conflicts with existing mapping in other types:
        // [mapper [xxx] has different [format] values]
        boolean putMapping = elasticsearchTemplate.putMapping(Agency.class);
        // 获取映射关系
        Map mapping = elasticsearchTemplate.getMapping(Agency.class);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("createIndex", createIndex);
        resultMap.put("putMapping", putMapping);
        resultMap.put("mapping", mapping);

        /**
         * <pre>
         *     {
         *   "mapping": {
         *     "properties": {
         *       "address": {
         *         "type": "text",
         *         "store": true,
         *         "analyzer": "ik_max_word",
         *         "search_analyzer": "ik_smart"
         *       },
         *       "agencyCode": {
         *         "type": "text",
         *         "store": true,
         *         "analyzer": "ik_max_word",
         *         "search_analyzer": "ik_smart"
         *       },
         *       "agencyId": {
         *         "type": "keyword"
         *       },
         *       "agencyName": {
         *         "type": "text",
         *         "store": true,
         *         "analyzer": "ik_max_word",
         *         "search_analyzer": "ik_smart"
         *       },
         *       "employeesCount": {
         *         "type": "long"
         *       },
         *       "establishmentTime": {
         *         "type": "date",
         *         "store": true
         *       }
         *     }
         *   },
         *   "putMapping": true,
         *   "createIndex": true
         * }
         * </pre>
         */
        return resultMap;
    }

    /**
     * http://localhost:8080/est/agency/deleteIndex
     * <p>
     * boolean deleteIndex(String indexName) ：删除指定名称的索引，其中的数据也会一起删除，相当于删库。
     * boolean deleteIndex(Class<T> clazz) ：删除实体中注解对应的索引，其中的数据也会一起删除，相当于删库。
     */
    @GetMapping("est/agency/deleteIndex")
    public Map<String, Object> deleteIndex() {
        boolean deleteIndex = elasticsearchTemplate.deleteIndex(Agency.class);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("deleteIndex", deleteIndex);

        return resultMap;
    }

    /**
     * 新增或更新 es 文档
     * http://localhost:8080/est/agency/saveOrUpdate
     *
     * <pre>
     * {"address": "汉东省芙蓉区芙蓉中路001号", "agencyCode": "001001", "agencyId": "78451ID0A1", "agencyName": "汉东省财政厅", "employeesCount": "524", "establishmentTime": "1995-05-06 12:16:14"}
     * {"address": "江东省天心区书院中路002号", "agencyCode": "001002", "agencyId": "78451ID0A2", "agencyName": "江东省教育厅", "employeesCount": "324", "establishmentTime": "1996-07-01 08:08:08"}
     * {"address": "总感觉，汽车的事情，很大。中国汽车工业能够往上走，世界会变得更美好。", "agencyCode": "001003", "agencyId": "78451ID0A3", "agencyName": "这个指标，中国超过日本，首次世界第一", "employeesCount": "1324", "establishmentTime": "2012-07-01 08:08:08"}
     * {"address": "汉东省雨花区车站南路101号", "agencyCode": "001004", "agencyId": "78451ID0A4", "agencyName": "汉东省公安厅", "employeesCount": "560", "establishmentTime": "1997-05-06 12:16:14"}
     * </pre>
     *
     * @param agency
     */
    @PostMapping("est/agency/saveOrUpdate")
    public Map<String, Object> saveOrUpdate(@RequestBody Agency agency) {
        IndexQuery indexQuery = new IndexQueryBuilder()
                .withId(agency.getAgencyId())
                .withObject(agency).build();
        // 为对象添加索引（保存或更新-主键存在时更新，否则新增），返回索引ID
        String index = elasticsearchTemplate.index(indexQuery);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("index", index);

        return resultMap;
    }

    /**
     * 批量新增或更新 es 文档
     * http://localhost:8080/est/agency/batchSaveOrUpdate
     *
     * <pre>
     * [
     * {"address": "力争到2023年底，21个兵团非优势棉区团场中：第二师24团、36团、37团、38团，第三师41团、东风农场、红旗农场，第四师62团、70团，第六师红旗农场，第十二师221团，第十三师柳树泉农场、淖毛湖农场，第十四师47团、皮山农场、224团等16个团场全部退出棉花种植；第一师5团、第四师69团、第五师84团、第六师101团、第七师131团等5个团场植棉面积不超过该团2020年种植面积。", "agencyCode": "001001", "agencyId": "78451ID0B1", "agencyName": "新疆生产建设兵团：16个团场力争今年年底退出棉花种植", "employeesCount": "5005", "establishmentTime": "2022-01-01 08:08:08"},
     * {"address": "江东省岳麓区岳麓大道101号", "agencyCode": "001003", "agencyId": "78451ID0B3", "agencyName": "江东省农业厅", "employeesCount": "1024", "establishmentTime": "2000-01-01 08:08:08"},
     * {"address": "江东省开福区芙蓉北路501号", "agencyCode": "001005", "agencyId": "78451ID0B5", "agencyName": "江东省税务厅", "employeesCount": "300", "establishmentTime": "1999-07-01 08:08:08"},
     * {"address": "江东省望城区月亮岛沿江路101号", "agencyCode": "001006", "agencyId": "78451ID0B6", "agencyName": "江东省行政大厅", "employeesCount": "1000", "establishmentTime": "2001-01-01 08:08:08"}
     * ]
     * </pre>
     *
     * @param agencyList
     */
    @PostMapping("est/agency/batchSaveOrUpdate")
    public Map<String, Object> batchSaveOrUpdate(@RequestBody List<Agency> agencyList) {
        List<IndexQuery> indexQueryList = new ArrayList<>(agencyList.size());
        IndexQuery indexQuery;
        for (Agency agency : agencyList) {
            indexQuery = new IndexQueryBuilder().withId(agency.getAgencyId()).withObject(agency).build();
            indexQueryList.add(indexQuery);
        }
        // 大容量索引所有对象（批量保存或更新-主键存在时更新，否则新增）
        elasticsearchTemplate.bulkIndex(indexQueryList);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "200");

        return resultMap;
    }

    /**
     * http://localhost:8080/est/agency/deleteById?agencyId=78451ID05
     * <p>
     * String delete(Class<T> clazz, String id) ：根据主键Id删除单条文档数据
     *
     * @param agencyId
     * @return
     */
    @GetMapping("est/agency/deleteById")
    public Map<String, Object> deleteById(@RequestParam String agencyId) {
        // 返回被删除的主键值，当数据不存在时，也会返回。
        String delete = elasticsearchTemplate.delete(Agency.class, agencyId);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("delete", delete);

        return resultMap;
    }

    /**
     * void delete(DeleteQuery deleteQuery, Class<T> clazz) : 删除与查询匹配的所有记录
     * <p>
     * http://localhost:8080/est/agency/deleteByQuery?keyWord=棉花
     *
     * @param keyWord：关键字
     * @return
     */
    @GetMapping("est/agency/deleteByQuery")
    public Map<String, Object> deleteByQuery(@RequestParam String keyWord) {
        QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(keyWord);
        DeleteQuery deleteQuery = new DeleteQuery();
        deleteQuery.setQuery(queryBuilder);
        elasticsearchTemplate.delete(deleteQuery, Agency.class);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", 200);
        return resultMap;
    }

    /**
     * 查询文档总数
     * http://localhost:8080/est/agency/count
     *
     * @return
     */
    @GetMapping("est/agency/count")
    public Map<String, Object> count() {
        //matchAllQuery 查询所有文档
        QueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        SearchQuery searchQuery = new NativeSearchQuery(queryBuilder);
        //count(SearchQuery searchQuery, Class<T> clazz): clazz 表示查询哪个索引与类型
        long count = elasticsearchTemplate.count(searchQuery, Agency.class);

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("count", count);

        return resultMap;
    }

    /**
     * 关键字 分词全文查询
     * http://localhost:8080/est/agency/queryStringQuery?keyWord=江东子弟多才俊
     * <pre>
     *     [
     *     {"agencyId":"78451ID03","agencyCode":"001003","agencyName":"江东省农业厅","establishmentTime":"2000-01-01 08:08:08","address":"江东省岳麓区岳麓大道101号","employeesCount":1024},
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324},
     *     {"agencyId":"78451ID06","agencyCode":"001006","agencyName":"江东省行政大厅","establishmentTime":"2001-01-01 08:08:08","address":"江东省望城区月亮岛沿江路101号","employeesCount":1000}
     *     ]
     * </pre>
     * http://localhost:8080/est/agency/queryStringQuery?keyWord=长沙市芙蓉中路
     * <pre>
     *     [
     *     {"agencyId":"78451ID01","agencyCode":"001001","agencyName":"汉东省财政厅","establishmentTime":"1995-05-06 12:16:14","address":"汉东省芙蓉区芙蓉中路001号","employeesCount":524},
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324}
     *     ]
     * </pre>
     *
     * @param keyWord
     * @return
     */
    @GetMapping("est/agency/queryStringQuery")
    public List<Agency> queryStringQuery(@RequestParam String keyWord, String page, String size) {
        // 如果传入的页码或者显示条数为空，或者是非法数字，则使用默认值。isNumeric 对负数("-100")也是返回 false
        page = page == null ? "0" : StringUtils.isNumeric(page) ? page : "0";
        size = size == null ? "10" : StringUtils.isNumeric(size) ? size : "10";
        // 分页设置
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(page), Integer.parseInt(size));

        // 关键字 分词全文查询
        QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(keyWord);
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryStringQueryBuilder)
                // 不分页时，默认返回前10个结果
                .withPageable(pageRequest)
                .build();
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);

        return agencyList;
    }

    /**
     * 单个字段 分词模糊查询
     * <p>
     * http://localhost:8080/est/agency/matchQuery?fieldName=agencyName&keyword=江东子弟多才俊&page=0&size=20
     * <pre>
     *     [
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324},
     *     {"agencyId":"78451ID03","agencyCode":"001003","agencyName":"江东省农业厅","establishmentTime":"2000-01-01 08:08:08","address":"江东省岳麓区岳麓大道101号","employeesCount":1024},
     *     {"agencyId":"78451ID06","agencyCode":"001006","agencyName":"江东省行政大厅","establishmentTime":"2001-01-01 08:08:08","address":"江东省望城区月亮岛沿江路101号","employeesCount":1000}
     *     ]
     * </pre>
     * http://localhost:8080/est/agency/matchQuery?fieldName=address&keyword=长沙市芙蓉中路&page=0&size=20
     * <pre>
     *     [
     *     {"agencyId":"78451ID01","agencyCode":"001001","agencyName":"汉东省财政厅","establishmentTime":"1995-05-06 12:16:14","address":"汉东省芙蓉区芙蓉中路001号","employeesCount":524},
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324}
     *     ]
     * </pre>
     *
     * @param fieldName
     * @param keyword
     * @param page
     * @param size
     * @return
     */
    @GetMapping("est/agency/matchQuery")
    public List<Agency> matchQuery(@RequestParam String fieldName,
                                   @RequestParam String keyword,
                                   String page, String size) {
        // 如果传入的页码或者显示条数为空，或者是非法数字，则使用默认值。isNumeric 对负数("-100")也是返回 false
        page = page == null ? "0" : StringUtils.isNumeric(page) ? page : "0";
        size = size == null ? "10" : StringUtils.isNumeric(size) ? size : "10";
        // 分页设置
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(page), Integer.parseInt(size));

        // 单个字段 分词模糊查询
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery(fieldName, keyword))
                .withPageable(pageRequest)
                .build();
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);
        return agencyList;
    }

    /**
     * 多字段 分词全文检索
     * http://localhost:8080/est/agency/multiMatchQuery?keyword=江东子弟多才俊
     * <pre>
     *     [
     *     {"agencyId":"78451ID03","agencyCode":"001003","agencyName":"江东省农业厅","establishmentTime":"2000-01-01 08:08:08","address":"江东省岳麓区岳麓大道101号","employeesCount":1024},
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324},
     *     {"agencyId":"78451ID06","agencyCode":"001006","agencyName":"江东省行政大厅","establishmentTime":"2001-01-01 08:08:08","address":"江东省望城区月亮岛沿江路101号","employeesCount":1000}
     *     ]
     * </pre>
     * http://localhost:8080/est/agency/multiMatchQuery?keyword=长沙市芙蓉中路
     * <pre>
     *     [
     *     {"agencyId":"78451ID01","agencyCode":"001001","agencyName":"汉东省财政厅","establishmentTime":"1995-05-06 12:16:14","address":"汉东省芙蓉区芙蓉中路001号","employeesCount":524},
     *     {"agencyId":"78451ID02","agencyCode":"001002","agencyName":"江东省教育厅","establishmentTime":"1996-07-01 08:08:08","address":"江东省天心区书院中路002号","employeesCount":324}
     *     ]
     * </pre>
     *
     * @param keyword 检索的关键字
     * @param page    页码，从0开始
     * @param size    每页的条数
     * @return
     */
    @GetMapping("est/agency/multiMatchQuery")
    public List<Agency> multiMatchQuery(@RequestParam String keyword, String page, String size) {
        //如果传入的页码或者显示条数为空，或者是非法数字，则使用默认值。isNumeric 对负数("-100")也是返回 false
        page = page == null ? "0" : StringUtils.isNumeric(page) ? page : "0";
        size = size == null ? "10" : StringUtils.isNumeric(size) ? size : "10";

        QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery(keyword, "agencyCode", "agencyName", "address");
        NativeSearchQuery searchQuery = new NativeSearchQuery(queryBuilder);
        // 分页设置
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(page), Integer.parseInt(size));
        searchQuery.setPageable(pageRequest);
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);
        return agencyList;
    }

    /**
     * 模糊查询，类似SQL中 like '%xxx%' 模糊匹配
     * <p>
     * http://localhost:8080/est/agency/matchPhraseQuery?fieldName=agencyName&keyword=江东公安厅&page=0&size=20
     * http://localhost:8080/est/agency/matchPhraseQuery?fieldName=address&keyword=江东公安厅&page=0&size=20
     *
     * @param fieldName
     * @param keyword
     * @param page
     * @param size
     * @return
     */
    @GetMapping("est/agency/matchPhraseQuery")
    public List<Agency> matchPhraseQuery(@RequestParam String fieldName,
                                         @RequestParam String keyword,
                                         String page, String size) {
        // 如果传入的页码或者显示条数为空，或者是非法数字，则使用默认值。isNumeric 对负数("-100")也是返回 false
        page = page == null ? "0" : StringUtils.isNumeric(page) ? page : "0";
        size = size == null ? "10" : StringUtils.isNumeric(size) ? size : "10";
        // 分页设置
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(page), Integer.parseInt(size));

        // matchPhraseQuery：模糊查询，类似SQL中的 like '%xxx%'
        // matchPhrasePrefixQuery：前缀模糊查询，类似SQL中的 like 'xxx%'
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchPhraseQuery(fieldName, keyword))
                .withPageable(pageRequest)
                .build();
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);
        return agencyList;
    }

    /**
     * term 匹配，即不分词匹配，传来什么值就会拿传的值去做完全匹配。
     * <p>
     * http://localhost:8080/est/agency/termQuery?agencyId=78451ID04
     *
     * <pre>
     *     [{"agencyId":"78451ID04","agencyCode":"001004","agencyName":"汉东省公安厅","establishmentTime":"1997-05-06 12:16:14","address":"汉东省雨花区车站南路101号","employeesCount":560}]
     * </pre>
     */
    @GetMapping("est/agency/termQuery")
    public List<Agency> termQuery(@RequestParam String agencyId) {
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("agencyId", agencyId);
        // 不对传来的值分词，去找完全匹配的
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(termQueryBuilder)
                // 不分页时，默认返回前10个结果
                // .withPageable()
                .build();
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);

        return agencyList;
    }

    /**
     * termsQuery ：相当于 sql 中的 in (x,x,x)
     * <p>
     * http://localhost:8080/est/agency/termsQuery
     * ["78451ID04", "78451ID02", "78451ID01", "78451ID01"]
     *
     * <pre>
     * [
     * {"address": "江东省天心区书院中路002号", "agencyCode": "001002", "agencyId": "78451ID02", "agencyName": "江东省教育厅", "employeesCount": 324, "establishmentTime": "1996-07-01 08:08:08"},
     * {"address": "汉东省雨花区车站南路101号", "agencyCode": "001004", "agencyId": "78451ID04", "agencyName": "汉东省公安厅", "employeesCount": 560, "establishmentTime": "1997-05-06 12:16:14"},
     * {"address": "汉东省芙蓉区芙蓉中路001号", "agencyCode": "001001", "agencyId": "78451ID01", "agencyName": "汉东省财政厅", "employeesCount": 524, "establishmentTime": "1995-05-06 12:16:14"}
     * ]
     * </pre>
     *
     * @param agencyIdList
     * @return
     */
    @PostMapping("est/agency/termsQuery")
    public List<Agency> termsQuery(@RequestBody List<String> agencyIdList) {
        TermsQueryBuilder termsQueryBuilder = QueryBuilders.termsQuery("agencyId", agencyIdList);
        // termsQuery ：相当于 sql 中的 in (x,x,x)
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(termsQueryBuilder)
                // 不分页时，默认返回前10个结果
                // .withPageable()
                .build();
        List<Agency> agencyList = elasticsearchTemplate.queryForList(searchQuery, Agency.class);

        return agencyList;
    }

}
